﻿/*
 * @author: wizardc
 */

using System;
using UnityEngine;
using UnityEngine.UI;

namespace Dou.UI
{
    /// <summary>
    /// ScrollRect 滚动到边缘的检测，支持 SuperScrollView
    /// SuperScrollView会根据可变尺寸内容动态改变Content的尺寸和位置，所以不能简单的判断Content的位置来确定是否滚动到底，
    /// 这里新加一个脚本每帧判断Content的四个角是否和Viewport的四个角重叠来判定是否滚动到底，
    /// SuperScrollView如果没到底四周都会存在预留的空间。
    /// </summary>
    public class ScrollRectEdge : MonoBehaviour
    {
        public enum EdgeType
        {
            Up,
            Down,
            Left,
            Right,
        }

        /// <summary>
        /// 目标滚动区域
        /// </summary>
        public ScrollRect scrollRect;

        /// <summary>
        /// 边缘误差范围
        /// </summary>
        public float offset = 5f;

        /// <summary>
        /// 滚动到边缘时事件，只会派发一次
        /// </summary>
        public event Action<EdgeType> onEdge;

        private bool[] _state = new bool[4];
        private Vector2 _lastPosition = Vector2.negativeInfinity;
        private Vector3[] _corners = new Vector3[4];

        /// <summary>
        /// 重置边缘事件的派发状态
        /// </summary>
        public void ResetState()
        {
            for (int i = 0; i < _state.Length; i++)
            {
                _state[i] = false;
            }
        }

        void Update()
        {
            if (scrollRect == null)
            {
                return;
            }
            Vector2 position = scrollRect.content.anchoredPosition;
            if (_lastPosition == position)
            {
                return;
            }
            _lastPosition = position;
            CheckEdge();
        }

        private void CheckEdge()
        {
            RectTransform viewport = scrollRect.viewport;
            viewport.GetLocalCorners(_corners);
            float up = _corners[2].y;
            float down = _corners[0].y;
            float left = _corners[2].x;
            float right = _corners[0].x;
            scrollRect.content.GetWorldCorners(_corners);
            Vector3 maxCorners = viewport.InverseTransformPoint(_corners[2]);
            Vector3 minCorners = viewport.InverseTransformPoint(_corners[0]);
            if (!_state[0])
            {
                if (maxCorners.y <= up + offset && maxCorners.y >= up - offset)
                {
                    _state[0] = true;
                    onEdge?.Invoke(EdgeType.Up);
                }
            }
            if (!_state[1])
            {
                if (minCorners.y <= down + offset && minCorners.y >= down - offset)
                {
                    _state[1] = true;
                    onEdge?.Invoke(EdgeType.Down);
                }
            }
            if (!_state[2])
            {
                if (maxCorners.x <= left + offset && maxCorners.x >= left - offset)
                {
                    _state[2] = true;
                    onEdge?.Invoke(EdgeType.Left);
                }
            }
            if (!_state[3])
            {
                if (minCorners.x <= right + offset && minCorners.x >= right - offset)
                {
                    _state[3] = true;
                    onEdge?.Invoke(EdgeType.Right);
                }
            }
        }
    }
}
